/***************************************************************************
 *   Copyright (C) 2002~2010 by Yuking                                     *
 *   yuking_net@sohu.com                                                   *
 *   Copyright (C) 2010~2010 by CSSlayer                                   *
 *   wengxt@gmail.com                                                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
 ***************************************************************************/

#include "fcitx/fcitx.h"

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xmd.h>
#include <X11/extensions/Xrender.h>
#include <dbus/dbus.h>

#include "TrayWindow.h"
#include "tray.h"
#include "skin.h"
#include "classicui.h"
#include "fcitx-utils/log.h"
#include "fcitx/frontend.h"
#include "fcitx/module.h"
#include "MenuWindow.h"
#include "fcitx/instance.h"
#include "fcitx-utils/utils.h"

static boolean TrayEventHandler(void *arg, XEvent* event);
static boolean dbus_showTooltip(char * text);

void TrayWindowInit(TrayWindow *trayWindow)
{
    FcitxClassicUI *classicui = trayWindow->owner;
    Display *dpy = classicui->dpy;
    int iScreen = classicui->iScreen;
    char   strWindowName[] = "Fcitx Tray Window";
    if (!classicui->bUseTrayIcon || classicui->isSuspend || classicui->notificationItemAvailable)
        return;

    if (trayWindow->window == None && trayWindow->dockWindow != None) {
        XVisualInfo* vi = TrayGetVisual(trayWindow);
        if (vi && vi->visual) {
            Window p = DefaultRootWindow(dpy);
            Colormap colormap = XCreateColormap(dpy, p, vi->visual, AllocNone);
            XSetWindowAttributes wsa;
            wsa.background_pixmap = 0;
            wsa.colormap = colormap;
            wsa.background_pixel = 0;
            wsa.border_pixel = 0;
            trayWindow->window = XCreateWindow(dpy, p, -1, -1, 22, 22,
                                            0, vi->depth, InputOutput, vi->visual,
                                            CWBackPixmap | CWBackPixel | CWBorderPixel | CWColormap, &wsa);
        } else {
            trayWindow->window = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
                                -1, -1, 22, 22, 0,
                                BlackPixel(dpy, DefaultScreen(dpy)),
                                WhitePixel(dpy, DefaultScreen(dpy)));
            XSetWindowBackgroundPixmap(dpy, trayWindow->window, ParentRelative);
        }
        if (trayWindow->window == (Window) NULL)
            return;

        trayWindow->size = 22;
        XSizeHints size_hints;
        size_hints.flags = PWinGravity | PBaseSize;
        size_hints.base_width = trayWindow->size;
        size_hints.base_height = trayWindow->size;
        XSetWMNormalHints(dpy, trayWindow->window, &size_hints);

        if (vi && vi->visual)
            trayWindow->cs_x = cairo_xlib_surface_create(dpy, trayWindow->window, trayWindow->visual.visual, 200, 200);
        else {
            Visual *target_visual = DefaultVisual(dpy, iScreen);
            trayWindow->cs_x = cairo_xlib_surface_create(dpy, trayWindow->window, target_visual, 200, 200);
        }
        trayWindow->cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);

        XSelectInput(dpy, trayWindow->window, ExposureMask | KeyPressMask |
                    ButtonPressMask | ButtonReleaseMask | StructureNotifyMask
                    | EnterWindowMask | PointerMotionMask | LeaveWindowMask | VisibilityChangeMask);

        ClassicUISetWindowProperty(classicui, trayWindow->window, FCITX_WINDOW_DOCK, strWindowName);

        TrayFindDock(trayWindow);
    }
}

TrayWindow* TrayWindowCreate(FcitxClassicUI *classicui)
{
    TrayWindow *trayWindow = fcitx_utils_malloc0(sizeof(TrayWindow));
    trayWindow->owner = classicui;
    TrayInitAtom(trayWindow);
    trayWindow->dockWindow = TrayGetDock(trayWindow);
    FcitxX11AddXEventHandler(classicui->owner, TrayEventHandler, trayWindow);
    /* We used to init trayWindow here, but now we should delay it */
    return trayWindow;
}

void TrayWindowRelease(TrayWindow *trayWindow)
{
    FcitxClassicUI *classicui = trayWindow->owner;
    Display *dpy = classicui->dpy;
    trayWindow->bTrayMapped = false;
    trayWindow->bMouseInTray = false;
    memset(&trayWindow->visual, 0, sizeof(trayWindow->visual));

    if (trayWindow->window == None)
        return;

    cairo_surface_destroy(trayWindow->cs);
    cairo_surface_destroy(trayWindow->cs_x);
    XDestroyWindow(dpy, trayWindow->window);
    trayWindow->window = None;
    trayWindow->cs = NULL;
    trayWindow->cs_x = NULL;
}

void TrayWindowDraw(TrayWindow* trayWindow)
{
    FcitxClassicUI *classicui = trayWindow->owner;
    FcitxSkin *sc = &classicui->skin;
    SkinImage *image;
    int f_state;
    if (!classicui->bUseTrayIcon)
        return;

    if (!trayWindow->bTrayMapped)
        return;
    /*  切换输入法时更新tooltip  */
    if(trayWindow->bMouseInTray)
    {
        FcitxIM* im = FcitxInstanceGetCurrentIM(classicui->owner);
        if (im)
            dbus_showTooltip(im->strName);
    }
    /*-----------------------*/

    if (FcitxInstanceGetCurrentState(classicui->owner) == IS_ACTIVE)
        f_state = ACTIVE_ICON;
    else
        f_state = INACTIVE_ICON;
    cairo_t *c;
    cairo_surface_t *png_surface;

    /* 画托盘图标，不支持svg */
    if (f_state) {
        image = GetIMIcon(classicui, sc, sc->skinTrayIcon.active, 2, true);
    } else {
        FcitxIM* im = FcitxInstanceGetCurrentIM(classicui->owner);
        if (!im)
            return;
        //如果首位输入法是fcitx-keyboard-*则显示小键盘图标
//        FcitxLog(FATAL, "TrayWindowDraw uniqueName = %s",im->uniqueName);
        if (strncmp(im->uniqueName, "fcitx-keyboard-",strlen("fcitx-keyboard-")) == 0) {
            if(trayWindow->size >= 16 && trayWindow->size <22)
                image = LoadImage(sc, sc->skinTrayIcon.inactive, true);
            else if(trayWindow->size >= 22 && trayWindow->size < 24)
                image = LoadImage(sc, sc->skinTrayIcon.inactive_22, true);
            else if(trayWindow->size >= 24 && trayWindow->size < 32)
                image = LoadImage(sc, sc->skinTrayIcon.inactive_24, true);
            else if(trayWindow->size >= 32 && trayWindow->size < 48)
                image = LoadImage(sc, sc->skinTrayIcon.inactive_32, true);
            else if(trayWindow->size >= 48 && trayWindow->size < 64)
                image = LoadImage(sc, sc->skinTrayIcon.inactive_48, true);
            else if(trayWindow->size >= 64 && trayWindow->size < 96)
                image = LoadImage(sc, sc->skinTrayIcon.inactive_64, true);
            else if(trayWindow->size >= 96 && trayWindow->size < 128)
                image = LoadImage(sc, sc->skinTrayIcon.inactive_96, true);
            else
                image = LoadImage(sc, sc->skinTrayIcon.inactive_128, true);
//            FcitxLog(FATAL, "TrayWindowDraw sc->skinTrayIcon.inactive = %s",sc->skinTrayIcon.inactive);
            if(image == NULL)//没获取到图标用皮肤默认inactive图标
                image = LoadImage(sc, sc->skinTrayIcon.inactive, true);
        }
        else
        {
            image = GetIMIcon(classicui, sc, sc->skinTrayIcon.active, 2, true);
        }
    }
    if (image == NULL)
        return;
    /* active, try draw im icon on tray */
    if (f_state && image) {

    }
    png_surface = image->image;

    c = cairo_create(trayWindow->cs);
    cairo_set_source_rgba(c, 1, 1, 1, 0);
    cairo_set_operator(c, CAIRO_OPERATOR_SOURCE);
    cairo_paint(c);

    do {
        if (png_surface) {
            int w = cairo_image_surface_get_width(png_surface);
            int h = cairo_image_surface_get_height(png_surface);
//            FcitxLog(FATAL, "TrayWindowDraw w= %d,h=%d",w,h);
            if (w == 0 || h == 0)
                break;
            double scaleW = 1.0, scaleH = 1.0;
            if (w > trayWindow->size || h > trayWindow->size)
            {
                scaleW = ((double) trayWindow->size) / w;
                scaleH = ((double) trayWindow->size) / h;
                if (scaleW > scaleH)
                    scaleH = scaleW;
                else
                    scaleW = scaleH;
            }
            int aw = scaleW * w;
            int ah = scaleH * h;

            cairo_scale(c, scaleW, scaleH);
            cairo_set_source_surface(c, png_surface, (trayWindow->size - aw) / 2 , (trayWindow->size - ah) / 2);
            cairo_set_operator(c, CAIRO_OPERATOR_OVER);
            cairo_paint_with_alpha(c, 1);
        }
    } while(0);

    cairo_destroy(c);

    XVisualInfo* vi = trayWindow->visual.visual ? &trayWindow->visual : NULL;
    if (!(vi && vi->visual)) {
        XClearArea(trayWindow->owner->dpy, trayWindow->window, 0, 0, trayWindow->size, trayWindow->size, False);
    }

    c = cairo_create(trayWindow->cs_x);
    if (vi && vi->visual) {
        cairo_set_source_rgba(c, 0, 0, 0, 0);
        cairo_set_operator(c, CAIRO_OPERATOR_SOURCE);
        cairo_paint(c);
    }
    cairo_set_operator(c, CAIRO_OPERATOR_OVER);
    cairo_set_source_surface(c, trayWindow->cs, 0, 0);
    cairo_rectangle(c, 0, 0, trayWindow->size, trayWindow->size);
    cairo_clip(c);
    cairo_paint(c);
    cairo_destroy(c);
    cairo_surface_flush(trayWindow->cs_x);
}

#define NAME_SIZE 64
#define UKUI_PANEL_DBUS_PATH "/"
#define UKUI_PANEL_DBUS_SERVICE "com.ukui.panel.desktop"
#define UKUI_PANEL_DBUS_INTERFACE "com.ukui.panel.desktop"

static boolean dbus_showTooltip(char * text)
{
    boolean ret_value;
    DBusError err;

    dbus_error_init(&err);
    DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
    if(dbus_error_is_set(&err)) {
         FcitxLog(WARNING, "dbus_bus_get Error (%s)", err.message);
         dbus_error_free(&err);
         return false;
    }

    dbus_error_init(&err);
    boolean ret = dbus_bus_name_has_owner(conn, UKUI_PANEL_DBUS_SERVICE, &err);
    if (dbus_error_is_set(&err)) {
        FcitxLog(WARNING, "dbus_bus_name_has_owner Error (%s)", err.message);
        dbus_error_free(&err);
        ret_value = false;
    }
    else
    {
     if (ret)
         ret_value = true;
     else
     {
         ret_value = false;
     }
    }

    if (ret_value == false) {
        dbus_connection_unref(conn);
        return false;
    }

    DBusMessage * msg = NULL;
    msg = dbus_message_new_method_call (UKUI_PANEL_DBUS_SERVICE ,
                     UKUI_PANEL_DBUS_PATH, UKUI_PANEL_DBUS_INTERFACE, "ShowTooltipText");
    if (msg == NULL) {
        dbus_connection_unref(conn);
        return false;
    }
    /*为消息添加函数的参数
    第一个参数：字符串类型的，用来显示对话框的提醒内容
    第二个参数：整数类型的，用来标志对话框的类型
    第三个参数：字符串类型的，用来显示确认按钮的内容
    注意：传递的都是参数值的地址
    */
//    const char * interface = UKUI_PANEL_DBUS_INTERFACE;
    FcitxLog(FATAL, "dbus_showTooltip %s",text);
    if (! dbus_message_append_args (msg ,
             DBUS_TYPE_STRING , &text ,
             DBUS_TYPE_INVALID )) {
        FcitxLog(FATAL,"Ran out of memory while constructing args/n");
        exit( EXIT_FAILURE );
    }

    if (NULL == dbus_connection_send_with_reply_and_block (conn , msg ,-1, NULL)) {
        dbus_connection_unref(conn);
        dbus_message_unref (msg);
        return false;
    }

    dbus_message_unref (msg);
    msg = NULL;

    // free the message
    dbus_connection_unref(conn);
    //dbus_connection_close(conn);

    FcitxLog(WARNING, "dbus_showTooltip: %d", ret_value);
    return ret_value;
}
/*
 *下上左右：0123
 *
 */
static int GetPanelPositionOrsize(Bool pos)
{
    DBusError err;
    dbus_error_init(&err);
    DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
    if(dbus_error_is_set(&err)) {
         FcitxLog(WARNING, "dbus_bus_get Error (%s)", err.message);
         dbus_error_free(&err);
         return -1;
    }

    dbus_error_init(&err);
    dbus_bus_name_has_owner(conn, UKUI_PANEL_DBUS_SERVICE, &err);
    if (dbus_error_is_set(&err)) {
        FcitxLog(WARNING, "dbus_bus_name_has_owner Error (%s)", err.message);
        dbus_error_free(&err);
        dbus_connection_unref(conn);
        return -1;
    }

    DBusMessage * msg = NULL;
    if(pos)
        msg = dbus_message_new_method_call (UKUI_PANEL_DBUS_SERVICE ,UKUI_PANEL_DBUS_PATH, UKUI_PANEL_DBUS_INTERFACE, "GetPanelPosition");
    else
        msg = dbus_message_new_method_call (UKUI_PANEL_DBUS_SERVICE ,UKUI_PANEL_DBUS_PATH, UKUI_PANEL_DBUS_INTERFACE, "GetPanelSize");
    if (msg == NULL) {
        dbus_connection_unref(conn);
        return -1;
    }
    /*为消息添加函数的参数
    第一个参数：字符串类型的，用来显示对话框的提醒内容
    第二个参数：整数类型的，用来标志对话框的类型
    第三个参数：字符串类型的，用来显示确认按钮的内容
    注意：传递的都是参数值的地址
    */
    char *text= "text";
    if (! dbus_message_append_args (msg ,
             DBUS_TYPE_STRING , &text,
             DBUS_TYPE_INVALID )) {
        FcitxLog(FATAL,"Ran out of memory while constructing args/n");
        exit( EXIT_FAILURE );
    }
    DBusMessage * reply = dbus_connection_send_with_reply_and_block (conn , msg ,-1, NULL);
    if (NULL == reply) {
        dbus_connection_unref(conn);
        dbus_message_unref (msg);
        return -1;
    }
    int iRet;
    dbus_message_get_args (reply, &err,DBUS_TYPE_INT32, &iRet,DBUS_TYPE_INVALID);

    dbus_message_unref (msg);
    msg = NULL;

    // free the messagedbus_message_append_args
    dbus_connection_unref(conn);
    //dbus_connection_close(conn);

//    FcitxLog(WARNING, "GetPanelPositionOrsize: %d", iRet);
    return iRet;
}

static boolean NotifyPanelFcitxTrayWid(uint32_t wid)
{
    DBusError err;
    dbus_error_init(&err);
    DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
    if(dbus_error_is_set(&err)) {
         FcitxLog(WARNING, "dbus_bus_get Error (%s)", err.message);
         dbus_error_free(&err);
         return false;
    }

    dbus_error_init(&err);
    dbus_bus_name_has_owner(conn, UKUI_PANEL_DBUS_SERVICE, &err);
    if (dbus_error_is_set(&err)) {
        FcitxLog(WARNING, "dbus_bus_name_has_owner Error (%s)", err.message);
        dbus_error_free(&err);
        dbus_connection_unref(conn);
        return false;
    }

    DBusMessage * msg = NULL;
    msg = dbus_message_new_method_call (UKUI_PANEL_DBUS_SERVICE ,UKUI_PANEL_DBUS_PATH, UKUI_PANEL_DBUS_INTERFACE, "SetFcitxTrayWid");
    if (msg == NULL) {
        dbus_connection_unref(conn);
        return false;
    }
    /*为消息添加函数的参数
    第一个参数：字符串类型的，用来显示对话框的提醒内容
    第二个参数：整数类型的，用来标志对话框的类型
    第三个参数：字符串类型的，用来显示确认按钮的内容
    注意：传递的都是参数值的地址
    */
    if (! dbus_message_append_args (msg ,
             DBUS_TYPE_INT32 , &wid,
             DBUS_TYPE_INVALID )) {
        FcitxLog(FATAL,"Ran out of memory while constructing args/n");
        exit( EXIT_FAILURE );
    }
    DBusMessage * reply = dbus_connection_send_with_reply_and_block (conn , msg ,-1, NULL);
    if (NULL == reply) {
        dbus_connection_unref(conn);
        dbus_message_unref (msg);
        return false;
    }
    dbus_message_unref (msg);
    // free the messagedbus_message_append_args
    dbus_connection_unref(conn);
    //dbus_connection_close(conn);

    return true;
}

boolean TrayEventHandler(void *arg, XEvent* event)
{
    TrayWindow *trayWindow = arg;
    FcitxClassicUI *classicui = trayWindow->owner;
    FcitxInstance* instance = classicui->owner;
    Display *dpy = classicui->dpy;
    if (!classicui->bUseTrayIcon)
        return false;
    switch (event->type) {
    case ClientMessage:
        if (event->xclient.message_type == trayWindow->atoms[ATOM_MANAGER]
            && event->xclient.data.l[1] == trayWindow->atoms[ATOM_SELECTION]
            && trayWindow->dockWindow == None) {
            trayWindow->dockWindow = event->xclient.data.l[2];
            TrayWindowRelease(trayWindow);
            TrayWindowInit(trayWindow);
            return true;
        }
        break;

    case Expose:
        if (event->xexpose.window == trayWindow->window) {
            TrayWindowDraw(trayWindow);
        }
        break;

    case EnterNotify:
        if (event->xexpose.window == trayWindow->window) {
            FcitxLog(FATAL, "TrayEventHandler EnterNotify");
            trayWindow->bMouseInTray = true;
            XlibMenu *mainMenuWindow = classicui->mainMenuWindow;
            if(!mainMenuWindow->visible)
            {
                FcitxIM* im = FcitxInstanceGetCurrentIM(instance);
                if (im)
                    dbus_showTooltip(im->strName);
            }
        }
        break;
    case LeaveNotify:
        if (event->xexpose.window == trayWindow->window) {
            FcitxLog(FATAL, "TrayEventHandler LeaveNotify");
            trayWindow->bMouseInTray = false;
            dbus_showTooltip("");
        }
        break;
    case ConfigureNotify:
        if (trayWindow->window == event->xconfigure.window) {
            int size = event->xconfigure.height;
            if (size != trayWindow->size) {
                trayWindow->size = size;
                FcitxLog(FATAL, "TrayEventHandler ConfigureNotify size=%d\n",size);
                XResizeWindow(dpy, trayWindow->window, size, size);
                XSizeHints size_hints;
                size_hints.flags = PWinGravity | PBaseSize;
                size_hints.base_width = trayWindow->size;
                size_hints.base_height = trayWindow->size;
                XSetWMNormalHints(dpy, trayWindow->window, &size_hints);
            }

            TrayWindowDraw(trayWindow);
            return true;
        }
        break;
    case ButtonPress: {
        if (event->xbutton.window == trayWindow->window) {
            switch (event->xbutton.button) {
            case Button1:
                FcitxInstanceChangeIMState(instance, FcitxInstanceGetCurrentIC(instance));
                break;
            case Button3: {
                XlibMenu *mainMenuWindow = classicui->mainMenuWindow;
                mainMenuWindow->anchor = MA_Tray;
                //下上左右：0123

//                int pos = GetPanelPositionOrsize(true);
//                int panelsize = GetPanelPositionOrsize(false);
//                if(pos != -1 && panelsize!= -1)
//                {
//                    if(pos == 0)
//                    {
//                        mainMenuWindow->trayX = event->xbutton.x_root - event->xbutton.x;
//                        mainMenuWindow->trayY = event->xconfigure.height-panelsize - 4;
//                    }
//                    if(pos == 1)
//                    {
//                        mainMenuWindow->trayX = event->xbutton.x_root - event->xbutton.x;
//                        mainMenuWindow->trayY = 0;
//                    }
//                    if(pos == 2)
//                    {
//                        mainMenuWindow->trayX = event->xbutton.x_root - event->xbutton.x;
////                        mainMenuWindow->trayY = ;
//                    }
//                    if(pos == 3)
//                    {
//                        mainMenuWindow->trayX = event->xbutton.x_root - event->xbutton.x;
////                        mainMenuWindow->trayY = ;
//                    }
//                }
                mainMenuWindow->trayX = event->xbutton.x_root - event->xbutton.x;
                mainMenuWindow->trayY = event->xbutton.y_root - event->xbutton.y;
                XlibMenuShow(mainMenuWindow);
                XSetInputFocus(mainMenuWindow->parent.owner->dpy, mainMenuWindow->parent.wId,RevertToParent,CurrentTime);
//                NotifyPanelFcitxTrayWid(mainMenuWindow->parent.wId);
            }
            break;
            }
            return true;
        }
    }
    break;
    case DestroyNotify:
        if (event->xdestroywindow.window == trayWindow->dockWindow) {
            trayWindow->dockWindow = TrayGetDock(trayWindow);
            trayWindow->bTrayMapped = False;
            TrayWindowRelease(trayWindow);
            if (trayWindow->dockWindow != None) {
                TrayWindowInit(trayWindow);
            }
            return true;
        }
        break;
    case PropertyNotify:
        if (event->xproperty.atom == trayWindow->atoms[ATOM_VISUAL] &&
            event->xproperty.window == trayWindow->dockWindow) {
            TrayWindowRelease(trayWindow);
            TrayWindowInit(trayWindow);
            return true;
        }
        break;
    }
    return false;
}
// kate: indent-mode cstyle; space-indent on; indent-width 0;
